package com.jason.common.file.word.resolver;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.jason.common.core.exception.BizException;
import com.jason.common.file.word.dto.WordImportQuestionData;
import com.jason.common.file.word.dto.WordTemplateTreeNode;
import com.jason.common.file.word.exception.WordImportException;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * 多线程Word文档Text解析器
 *
 * @author guozhongcheng
 * @since 2023/10/8
 **/
@Slf4j
public class ConcurrentWordTextResolver implements WordTextResolverInterface {
    /**
     * 最小并发Word文本字符长度
     */
    private static final int MIN_CONCURRENT_WORD_TEXT_LENGTH = 500000;
    /**
     * 线程池
     */
    private ThreadPoolExecutor threadPoolExecutor;
    /**
     * 试题数据列表
     */
    private final List<WordImportQuestionData> CONCURRENT_LIST = new CopyOnWriteArrayList<>();
    /**
     * 线程数
     */
    private int threadNum;

    public int getThreadNum() {
        return this.threadNum;
    }

    @Override
    public List<WordImportQuestionData> parse(String wordTextStr, List<WordTemplateTreeNode> templateTreeNodeList) {
        return new DefaultWordTextResolver().parse(wordTextStr, templateTreeNodeList);
    }

    @Override
    public List<WordImportQuestionData> concurrentParse(String wordTextStr, List<WordTemplateTreeNode> templateTreeNodeList, ThreadPoolExecutor threadPoolExecutor) throws Throwable {
        try {
            this.threadPoolExecutor = threadPoolExecutor;
            for (WordTemplateTreeNode templateTreeNode : templateTreeNodeList) {
                String nodeName = templateTreeNode.getNodeName();
                int index = wordTextStr.indexOf(nodeName);
                if (index != -1) {
                    templateTreeNode.setIndexOf(index);
                }
            }
            // 过滤数据并降序
            List<WordTemplateTreeNode> filterTemplateTreeNodeList = templateTreeNodeList.stream().filter(n -> n.getIndexOf() != -1).sorted(Comparator.comparing(WordTemplateTreeNode::getIndexOf).reversed()).collect(Collectors.toList());
            Assert.notEmpty(filterTemplateTreeNodeList, "缺少试卷名称节点信息, 节点标识符:{}",
                    StrUtil.join("|", templateTreeNodeList.stream().map(WordTemplateTreeNode::getNodeName).collect(Collectors.toList())));

            // 解析试卷数据
            this.doParsePaperData(wordTextStr, filterTemplateTreeNodeList);
        } catch (Exception e) {
            if (e instanceof IllegalArgumentException) {
                throw new WordImportException(e.getMessage(), wordTextStr);
            } else if (e instanceof WordImportException) {
                throw e;
            } else if (e instanceof BizException) {
                throw e;
            } else if (e instanceof CompletionException) {
                CompletionException ex4 = (CompletionException) e;
                throw ex4.getCause();
            }
            throw new BizException("并发解析Text发生错误," + e.getMessage(), e);
        }
        return this.CONCURRENT_LIST;
    }

    private void doParsePaperData(String wordTextStr, List<WordTemplateTreeNode> templateTreeNodeList) {
        int endIndex = -1;
        for (WordTemplateTreeNode treeNode : templateTreeNodeList) {
            // 构建试题目录结构
            int indexOf = treeNode.getIndexOf();
            String nodeName = treeNode.getNodeName();
            if (endIndex == -1) {
                endIndex = wordTextStr.length();
            }
            int beginIndex = indexOf + nodeName.length();
            String subText = StrUtil.sub(wordTextStr, beginIndex, endIndex);
            treeNode.setBeginIndex(beginIndex);
            treeNode.setEndIndex(endIndex);
            treeNode.setSubText(subText);

            if (StrUtil.isBlank(subText)) {
                continue;
            }
            Assert.notEmpty(treeNode.getChildrenList(), "模板对象配置试卷下缺失小节或大题");
            endIndex = beginIndex - nodeName.length();
        }
        // 执行并发解析
        this.doConcurrentParse(templateTreeNodeList);
    }

    /**
     * 执行并发解析
     *
     * @param templateTreeNodeList 模板树节点列表
     */
    private void doConcurrentParse(List<WordTemplateTreeNode> templateTreeNodeList) {
        // 计算多线程数，返回线程执行列表
        List<List<WordTemplateTreeNode>> calculateList = this.calculate(templateTreeNodeList);
        Assert.notEmpty(calculateList, "计算多线程数，返回线程执行列表为空");
        this.threadNum = calculateList.size();
        if (this.threadNum > 1) {
            List<CompletableFuture<List<WordImportQuestionData>>> futureList = new ArrayList<>(16);
            // 核心多线程处理
            for (List<WordTemplateTreeNode> wordTemplateTreeNodeList : calculateList) {
                CompletableFuture<List<WordImportQuestionData>> future = CompletableFuture.supplyAsync(() -> {
                    List<WordImportQuestionData> voList = new ArrayList<>();
                    for (WordTemplateTreeNode templateTreeNode : wordTemplateTreeNodeList) {
                        DefaultWordTextResolver wordTextResolver = new DefaultWordTextResolver();
                        wordTextResolver.concurrentDoParsePaperTitleData(templateTreeNode);
                        voList.addAll(wordTextResolver.getResultList());
                    }
                    return voList;
                }, this.threadPoolExecutor);
                futureList.add(future);
            }
            // 并发执行
            List<List<WordImportQuestionData>> collect = futureList.stream().map(CompletableFuture::join).collect(Collectors.toList());
            for (List<WordImportQuestionData> wordImportQuestionDataList : collect) {
                this.CONCURRENT_LIST.addAll(wordImportQuestionDataList);
            }
        } else {
            // 主线程执行
            for (WordTemplateTreeNode templateTreeNode : templateTreeNodeList) {
                DefaultWordTextResolver wordTextResolver = new DefaultWordTextResolver();
                wordTextResolver.concurrentDoParsePaperTitleData(templateTreeNode);
                this.CONCURRENT_LIST.addAll(wordTextResolver.getResultList());
            }
        }
    }

    /**
     * 计算多线程数，返回线程执行列表
     *
     * @param templateTreeNodeList 模板树节点列表
     * @return 线程执行列表
     */
    private List<List<WordTemplateTreeNode>> calculate(final List<WordTemplateTreeNode> templateTreeNodeList) {
        long textTotalLength = templateTreeNodeList.stream().map(WordTemplateTreeNode::getSubText).mapToLong(String::length).sum();
        List<List<WordTemplateTreeNode>> resultList = new ArrayList<>(16);
        // 文本字符串小于50万则不多线程执行
        if (textTotalLength < MIN_CONCURRENT_WORD_TEXT_LENGTH) {
            resultList.add(templateTreeNodeList);
            return resultList;
        }
        Map<Integer, Integer> map = new TreeMap<>();
        int maxProportion = 0;
        int maxProportionIndex = 0;
        for (int i = 0; i < templateTreeNodeList.size(); i++) {
            int length = templateTreeNodeList.get(i).getSubText().length();
            // 计算占比
            int value = new BigDecimal(length).divide(new BigDecimal(textTotalLength), 2, RoundingMode.HALF_DOWN).multiply(new BigDecimal(100)).intValue();
            map.put(i, value);
            if (maxProportion == 0) {
                maxProportion = value;
                maxProportionIndex = i;
            }
            if (value > maxProportion) {
                maxProportion = value;
                maxProportionIndex = i;
            }
        }
        int threadTotal;
        if (maxProportion > 0 && maxProportion <= 33) {
            threadTotal = 3;
        } else if (maxProportion > 33 && maxProportion <= 70) {
            threadTotal = 2;
        } else if (maxProportion > 70 && maxProportion <= 100) {
            threadTotal = 1;
        } else {
            throw new BizException("占比值【" + maxProportion + "】计算最大占比错误");
        }

        if (threadTotal == 1) {
            resultList.add(templateTreeNodeList);
        } else if (threadTotal == 2) {
            resultList.add(Collections.singletonList(templateTreeNodeList.get(maxProportionIndex)));
            // 创建第二个线程执行的数据
            List<WordTemplateTreeNode> list2 = new ArrayList<>(16);
            for (Integer index : map.keySet()) {
                Integer proportion = map.get(index);
                if (proportion != maxProportion) {
                    list2.add(templateTreeNodeList.get(index));
                }
            }
            resultList.add(list2);
        } else if (threadTotal == 3) {
            // 创建第一个线程执行的数据
            List<WordTemplateTreeNode> list1 = Collections.singletonList(templateTreeNodeList.get(maxProportionIndex));
            resultList.add(list1);
            // 创建第二个线程执行的数据
            List<WordTemplateTreeNode> list2 = new ArrayList<>(16);
            int capacity = 0;
            for (Integer index : map.keySet()) {
                Integer proportion = map.get(index);
                if (proportion != maxProportion) {
                    capacity += proportion;
                    if (capacity <= 33) {
                        list2.add(templateTreeNodeList.get(index));
                    } else {
                        resultList.add(list2);
                        break;
                    }
                }
            }
            // 创建第三个线程执行的数据
            List<WordTemplateTreeNode> list3 = CollUtil.subtractToList(CollUtil.subtractToList(templateTreeNodeList, list1), list2);
            resultList.add(list3);
        } else {
            throw new BizException("threadTotal不匹配");
        }
        return resultList;
    }

}
